<template>
  <!-- 移动端目录遮罩 -->
  <div
    v-if="isMobile && showCatalog"
    class="fixed inset-0 bg-black bg-opacity-50 z-50 flex justify-end transition-all duration-300"
    @click.self="showCatalog = false"
  >
    <div class="bg-white dark:bg-gray-800 w-[260px] h-full p-4 shadow-lg overflow-y-auto">
      <TOCList :toc-list="tocList" :active-id="activeId" @click-item="scrollTo" />
    </div>
  </div>

  <!-- 移动端目录按钮 -->
  <div v-if="isMobile" class="fixed bottom-4 right-16 z-50">
    <button
      class="bg-blue-600 hover:bg-blue-600 text-white px-3 py-1 rounded-full shadow-lg"
      @click="showCatalog = true"
    >
      目录
    </button>
  </div>

  <!-- 桌面端目录侧栏 -->
  <div v-else class="w-[320px] hidden lg:block mr-2">
    <div
      :class="[
        'sticky mt-8 text-sm/[30px] w-full bg-transparent border-none shadow-none bg-white border border-gray-200 rounded-2xl dark:bg-gray-800 dark:border-gray-700',
        topClass
      ]"
    >
      <!-- <div v-else class="w-full">
    <div
      :class="[
        'sticky text-sm w-full bg-white border border-gray-200 rounded-2xl dark:bg-gray-800 dark:border-gray-700 shadow-lg',
        topClass
      ]"
    > -->
      <Card title="文章目录" prefixIcon="mulu">
        <TOCList :toc-list="tocList" :active-id="activeId" @click-item="scrollTo" />
      </Card>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'

const props = defineProps<{
  contentHtml?: string
  mainRef?: HTMLElement | null
}>()

const tocList = ref<{ id: string; text: string; level: number }[]>([])
const activeId = ref<string | null>(null)
const showCatalog = ref(false)
const isMobile = ref(false)

const safeHtml = computed(() => props.contentHtml || '')

// 生成目录列表
watch(
  () => safeHtml.value,
  async newHtml => {
    tocList.value = []
    activeId.value = null
    if (!newHtml) return

    const container = document.createElement('div')
    container.innerHTML = newHtml
    const headers = container.querySelectorAll('h1, h2, h3, h4, h5')
    const idSet = new Set<string>()

    headers.forEach(header => {
      let id = header.id
      if (!id) {
        id =
          (header.textContent || '')
            .toLowerCase()
            .trim()
            .replace(/\s+/g, '-')
            .replace(/[^\w\-]/g, '') || ''
        let uniqueId = id
        let counter = 1
        while (idSet.has(uniqueId)) {
          uniqueId = `${id}-${counter++}`
        }
        id = uniqueId
        header.id = id
      }
      idSet.add(id)
      tocList.value.push({
        id,
        text: header.textContent || '',
        level: parseInt(header.tagName.charAt(1))
      })
    })

    await nextTick()
    setTimeout(onScroll, 60)
  },
  { immediate: true }
)

// 根据是否传了 mainRef 动态设置侧栏距离顶部的 class
const topClass = computed(() => (props.mainRef ? 'top-[0.5rem]' : 'top-[5.5rem]'))

// 滚动时高亮当前标题
function onScroll() {
  if (!tocList.value.length) return
  let currentId: string | null = null

  if (props.mainRef) {
    const container = props.mainRef
    const containerTop = container.getBoundingClientRect().top
    for (const item of tocList.value) {
      const el = document.getElementById(item.id)
      if (el) {
        const elTop = el.getBoundingClientRect().top
        if (elTop - containerTop <= 100) currentId = item.id
      }
    }
  } else {
    for (const item of tocList.value) {
      const el = document.getElementById(item.id)
      if (el) {
        if (el.getBoundingClientRect().top <= 100) currentId = item.id
      }
    }
  }

  if (currentId) activeId.value = currentId
}

// 点击目录滚动
function scrollTo(id: string) {
  const el = document.getElementById(id)
  if (el) {
    el.scrollIntoView({ behavior: 'smooth', block: 'start' })
    history.replaceState(null, '', `#${id}`)
    showCatalog.value = false
  }
}

function updateIsMobile() {
  isMobile.value = window.innerWidth < 1024
}

onMounted(() => {
  if (props.mainRef) {
    props.mainRef.addEventListener('scroll', onScroll, true)
  } else {
    window.addEventListener('scroll', onScroll, { passive: true })
  }
  updateIsMobile()
  window.addEventListener('resize', updateIsMobile)
})

onUnmounted(() => {
  if (props.mainRef) {
    props.mainRef.removeEventListener('scroll', onScroll)
  } else {
    window.removeEventListener('scroll', onScroll)
  }
  window.removeEventListener('resize', updateIsMobile)
})
</script>
